import os
import shutil
import tempfile
import urlparse

from orioluspith.archivers.zip import ZipError
from orioluspith.archivers.factory import PacketExtractorFactory, PacketExtractorFactoryError
from orioluspith.publicator.stower import EmptyStower
from orioluspith.packets.cryptography import Cryptography, CryptographyError
from orioluspith.packets.meta import Meta
from orioluspith.packets.metareader import FileMetaReader
from oriolustrotter.core import PacketDescriptor, PacketList, Downloader, DownloadError
from oriolustrotter.fetchers.wgetfetcher import WgetFetcher


class StorerError(Exception):
    def __init__(self, message):
        Exception.__init__(self, message)

class Storer:
    __PACKETS_LIST = "packetlist.yaml"
    
    def __init__(self, store_path, packet_list_path = None, remote_packet_list_url = None, stower = EmptyStower, keyring = None, secret_keyring = None):
        self.__store_path = store_path
        self.__remote_packet_list_url = remote_packet_list_url
        self.__stower = stower
        self.__keyring = keyring
        self.__secret_keyring = secret_keyring
        self.__tmp_list = []
        self.__base_packet_list_name = os.path.join(self.__store_path, self.__PACKETS_LIST)
        self.__packet_list = PacketList()
        self.__cache_path = tempfile.mktemp()
        
        if os.path.exists(self.__base_packet_list_name):
            self.__packet_list.load(self.__base_packet_list_name)
        if packet_list_path and os.path.isfile(packet_list_path):
            self.__packet_list.load(packet_list_path)
        if self.__remote_packet_list_url:
            if self.__remote_packet_list_url[-1] != "/":
                self.__remote_packet_list_url = self.__remote_packet_list_url + "/"
            
            packet_list_url = urlparse.urljoin(self.__remote_packet_list_url, self.__PACKETS_LIST)
            download_cache = tempfile.mktemp()
            packet_list_path = tempfile.mktemp()
            downloader = Downloader(download_cache, packet_list_path, WgetFetcher)
            try:
                downloader.get(packet_list_url)
            except DownloadError, message:
                raise StorerError("Getting remote packetlist was failed. Cause: %s" % message)
        
            remote_packet_list_path = os.path.join(packet_list_path, self.__PACKETS_LIST)
            if os.path.exists(remote_packet_list_path):
                self.__packet_list.load(remote_packet_list_path)
        
        if not os.path.exists(self.__store_path):
            os.makedirs(self.__store_path)
    
    def add_packet(self, packet_path, base_url, sign = False, password = None):
        __PACKET_SUFFIX = ".ori"
        if os.path.isdir(packet_path):
            for root, dirs, files in os.walk(packet_path):
                for dir in dirs:
                    if dir.startswith("."):
                        dirs.remove(dir)
                        
                for name in files:
                    if not name.startswith(".") and len(name) > len(__PACKET_SUFFIX) and name.find(__PACKET_SUFFIX) != -1:
                        iter_packet_path = os.path.join(packet_path, name)
                        self.__add_packet_to_list(iter_packet_path, base_url, sign, password)
                        
        elif os.path.isfile(packet_path) and packet_path.find(__PACKET_SUFFIX) != -1:
            self.__add_packet_to_list(packet_path, base_url, sign, password)
        else:
            raise StorerError("Invalid packet path was added to storer.")
            
        self.__set_packet_list(sign, password)
    
    def get_packetlist(self, packetlist_path):
        pass
    
    def exists_packet(self, packet_path):
        meta = self.__get_meta(packet_path)
        packet = self.__packet_list.get_packet(meta.name, meta.version)
        return None != packet
    
    def get_packet_info(self, name, version):
        return self.__packet_list.get_packet(name, version)
    
    def stow_packets(self, base_url, username, password):
        pass
    
    def __set_packet_list(self, sign = False, password = None):
        if len(self.__tmp_list) > 0:
            self.__packet_list.add_packets(self.__tmp_list)
            self.__packet_list.save(self.__base_packet_list_name)
            self.__tmp_list = []
        if sign:
            self.__sign_packet(self.__base_packet_list_name, self.__keyring, self.__secret_keyring, password)
    
    def __add_packet_to_list(self, packet_path, base_url, sign = False, password = None):
        meta = self.__get_meta(packet_path)
        packet_url = urlparse.urljoin(base_url, os.path.basename(packet_path))
        self.__tmp_list.append(PacketDescriptor(meta.name, meta.version, [packet_url]))
        
        dest_name = os.path.join(self.__store_path, os.path.basename(packet_path))
        shutil.copy(packet_path, dest_name)
        if sign:
            self.__sign_packet(dest_name, self.__keyring, self.__secret_keyring, password)

    def __get_meta(self, packet_path):
        try:
            if not os.path.exists(self.__cache_path):
                os.makedirs(self.__cache_path)
            
            try:
                archiver = PacketExtractorFactory().get_extractor(packet_path)
                archiver.extract(packet_path, self.__cache_path)
            except PacketExtractorFactoryError, message:
                raise StorerError(message)
            except ZipError, message:
                raise StorerError(message)

            meta_filename = os.path.join(self.__cache_path, "META")
            meta = FileMetaReader().read(meta_filename)
            
            return meta            
        finally:
            if os.path.exists(self.__cache_path):
                shutil.rmtree(self.__cache_path)

    def __sign_packet(self, packet_name, keyring = None, secret_keyring = None, key_password = None):
        crypto = Cryptography(keyring = keyring)
        try:
            if secret_keyring:
                crypto.create_signature_by_keyring(packet_name, secret_keyring, key_password)
            elif key_password != None:
                crypto.create_signature_by_default_keyring(packet_name, key_password)
            else:
                crypto.create_signature(packet_name)
                
        except CryptographyError, message:
            StorerError("Cryptography error was raised: %s" % message)
        